// 建立在 std::error::Error 基础上的灵活的具体错误类型
// 该库提供了 anyhow::Error，这是一种基于特质对象的错误类型，可在 Rust 应用程序中轻松进行惯用的错误处理。
// 使用 Result<T, anyhow::Error> 或等价的 anyhow::Result<T> 作为任何易错函数的返回类型。
// 在函数内部，使用? 来轻松传播任何实现 std::error::Error 特性的错误。

use std::io;
use std::path::PathBuf;
use anyhow::{anyhow, bail, Context, Error};

fn main() {
    println!("Hello, world!");

    let file_path = file!(); // 获取当前文件的路径。
    // Option<T> 类型可以用 unwrap_or_else 方法进行默认值设置。必须使用 match 模式匹配进行处理
    // Result<T, E> 类型可以用 map_err 方法进行错误转换。也必须使用 match 模式匹配进行处理
    let path = PathBuf::from(file_path);
    if let Some(parent) = path.parent() {
        println!("Parent path: {}", parent.display());
        let cluster_path = parent.join("cluster.json"); // 构建一个路径。
        let config = std::fs::read_to_string(cluster_path).unwrap();
        println!("Cluster config: {}", config);
    } else {
        println!("Failed to get parent path.");
    }

    // 调用 `read_file_content` 函数并处理结果。如果文件路径没有前缀则默认读取根目录下的 example.txt 文件。
    match read_file_content("example.txt") {
        Ok(content) => println!("File content: {}", content),
        Err(e) => eprintln!("Error: {}", e),
    }
}

// 存在返回值类型为 Result<T, anyhow::Error> 的函数。
// 使用默认的错误类型 anyhow::Error 进行错误处理。我们可以直接使用? 运算符来传播错误。
// 该函数读取文件内容并返回一个 Result<String, anyhow::Error> 类型。错误类型不需要指定，因为 anyhow::Error 实现了 std::error::Error 特性。
// 定义一个函数，该函数可能会失败并返回一个错误
fn read_file_content(file_path: &str) -> anyhow::Result<String> {
    // 使用 `std::fs::read_to_string` 读取文件内容
    let content = std::fs::read_to_string(file_path)
        .with_context(|| format!("Failed to read file: {}", file_path))?;
    Ok(content)
}

// 不存在返回值类型为 Result<(), anyhow::Error> 的函数。
// 我们可以使用 anyhow::Error 构建一次性错误信息。
fn read_file_content_with_return() -> anyhow::Result<()> {
    if false {
        Ok(())
    } else {
        Err(anyhow!("Something went wrong"))
    }
}

// 定义一个函数，返回 Result<T, anyhow::Error> 类型。anyhow::Error 时默认的错误类型。可以省略
fn get_cluster_info() -> anyhow::Result<(), anyhow::Error> {
    let config = std::fs::read_to_string("cluster.json")?;
    // let map = serde_json::from_str(&config).unwrap();
    // println!("Cluster info: {:#?}", map);

    let missing = String::from("Hello, world!"); // 错误：类型不匹配。
    // 可以使用 anyhow! 宏构建一次性错误信息，该宏支持字符串插值并生成 anyhow::Error.
    return Err(anyhow!("Missing attribute: {}", missing));

    // 提供了一个 bail! 宏，作为同样的提前返回的速记。
    bail!("Missing attribute: {}", missing);
}


// 源错误链的迭代器。该类型是 Error::chain 返回的迭代器。
pub fn underlying_io_error_kind(error: &Error) -> Option<io::ErrorKind> {
    for cause in error.chain() {
        if let Some(io_error) = cause.downcast_ref::<io::Error>() {
            return Some(io_error.kind());
        }
    }
    None
}

use thiserror::Error;

// 使用 thiserror 定义一个自定义错误类型
// Anyhow 适用于任何具有 std::error::Error 内隐的错误类型，包括在你的板块中定义的错误类型。我们没有捆绑派生（Error）宏，但你可以自己编写 impls 或使用类似 thiserror 的独立宏。
#[derive(Error, Debug)]
pub enum FormatError {
    #[error("Invalid header (expected {expected:?}, got {found:?})")]
    InvalidHeader {
        expected: String,
        found: String,
    },
    #[error("Missing attribute: {0}")]
    MissingAttribute(String),
}

fn get_cluster_info_with_custom_error() -> anyhow::Result<String, FormatError> {
    if true {
        Ok((String::from("Hello, world!")))
    } else {
        // 当出现错误时，返回自定义错误类型.需要将自定义错误类型转换为 anyhow::Error.
        Err((FormatError::MissingAttribute("missing attribute".to_string())))
    }
}


/*
1. anyhow 是什么？
anyhow是一个Rust库，用于简化错误处理和提供更好的错误报告。这个库适合用于应用程序，而不是用于创建库，因为它提供了一个非结构化的，方便使用的错误类型。
2. 与Rust的标准库对比
Rust的标准库提供了Result和Option类型用于错误处理，但它们通常需要指定错误类型。与此不同，anyhow::Result允许更简单地创建和处理错误。
3. anyhow优势
anyhow::Result可以包含任何实现了std::error::Error的类型，可以更容易地与其他库互操作。
4、anyhow的局限性和替代方案
anyhow主要用于应用程序的错误处理。如果你正在开发一个库，更好的选择可能是使用thiserror，因为它提供了更多的灵活性来定义结构化的错误类型。
*/
// 任何返回标准库 Result 类型的函数都可以轻松地改为返回 anyhow::Result 类型。
// 例如，以下代码片段展示了如何将标准库的 io::Error 转换为 anyhow::Error：


// 使用参看： https://juejin.cn/post/7272015112818393148
// 使用标准库的 Result
fn do_something() -> Result<(), std::io::Error> {
    //...
    Ok(())
}

// 使用 anyhow::Result
fn do_something_anyhow() -> anyhow::Result<()> {
    //...
    Ok(())
}

// anyhow 可以很容易地将使用thiserror定义的错误类型与anyhow结合使用。
// 定义一个自定义错误类型
#[derive(thiserror::Error, Debug)]
enum MyError {
    #[error("failed to read string")]
    ReadError(#[from] std::io::Error),
}

// 使用anyhow::Result
fn do_something_with_custom_error() -> anyhow::Result<()> {
    if true {
        Ok(())
    } else {
        // 当出现错误时，返回自定义错误类型.需要将自定义错误类型转换为 anyhow::Error.
        Err(Error::from(MyError::ReadError(std::io::Error::new(std::io::ErrorKind::Other, "oh no!"))))
    }
}